package SMSGUI;

import java.awt.Color;
import java.awt.FileDialog;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.KeyStroke;

import SMSGUI.readDBF;

/**
 * 
 * @author Roman SUCCES DERMEE HOOR JONGENS HEE JA. DUS. Ok ff korte uitleg, het
 *         is 2 uur snachts ik ben moe en ik voel me als die dude uit avatar met
 *         zn videologs... maar dan op papier. ofzo.
 * 
 *         De main staat onderin, Daarin worden eerst de Files die ik nodig heb
 *         opgehaald. Dit zorgt waarschijnlijk voor een OutOfMemorError, het
 *         duurt namelijk nogal lang om die Files te VerHashMappen (help). Als
 *         dit gedaan is zal hij de twee HashMaps weer verHashMapperen in een
 *         HashMap genaamd input (de classes die dit doen zijn respectievelijk
 *         fileToHashMap en compareMaps). Dus als dit werkt krijg je
 *         uiteindelijk in 'input' een HashMap met als Key de steden uit de
 *         tweeds, en als value een array met respectievelijk de latitude, de
 *         longitude en het sentiment. De coordinaten worden naar pixels in het
 *         plaatje omgezet met coordsToPixel(float lat, float lng), en het
 *         sentiment naar een kleur met sentimentToColor ofzo. De waarden in
 *         sentimentToColor zijn trouwens discussieerbaar, ik heb voor nu maar
 *         wat moois bedacht. Ok. (jullie denken mss dat ik ten tijden van het
 *         schrijven van dit ding dronken ben, maar dat valt wel mee) Ok. Dus
 *         hij heeft nu een kleur en pixels. Nu gaat hij met set(int i, int j,
 *         Color c) het plaatje inkleuren. Ik heb de coordinaat zelf, en + 2
 *         alle kanten op ingekleurd, als meer/minder nodig is is dat simpel om
 *         aan te passen enzo.
 * 
 *         Dus dat.
 */
public final class Picture implements ActionListener {
	private BufferedImage image; // the rasterized image
	private static JFrame frame; // on-screen view
	private String filename; // name of file
	private boolean isOriginUpperLeft = true; // location of origin
	private int width; // width and height
	private static int height;

	public Picture(String filename) {
		this.filename = filename;
		try {
			// try to read from file in working directory
			File file = new File(filename);
			if (file.isFile()) {
				image = ImageIO.read(file);
			}

			// now try to read from file in same directory as this .class file
			else {
				URL url = getClass().getResource(filename);
				if (url == null) {
					url = new URL(filename);
				}
				image = ImageIO.read(url);
			}
			width = image.getWidth();
			height = image.getHeight();
		} catch (IOException e) {
			// e.printStackTrace();
			throw new RuntimeException("Could not open file: " + filename);
		}
	}

	/**
	 * Converts a sentiment value to a color
	 */
	public Color sentimentToColor(Double sentiment) {
		Color color = null;
		if (sentiment < -1.0) //VERY NEGATIVE = RED
			color = Color.cyan;
		else if (sentiment >= -1.0 && sentiment <= -0.2) //NEGATIVE = ORANGE
			color = Color.blue;
		else if (sentiment > -0.2 && sentiment < 0.2)//NEUTRAL = CYAN
			color = Color.orange;
		else if (sentiment >= 0.2 && sentiment <= 1) //POSITIVE = BLUE
			color = Color.red;
		else if (sentiment > 1) //VERY POSITIVE = GREEN
			color = Color.magenta;
		return color;
	}

	/**
	 * converts a GPS coordinate to a pixel value optimised for kerstdiner
	 * 
	 */
	// lat long
	// amsterdam 5820.0 6840.0 377 483
	// apeldoorn 5256.0 10692.0 636 534
	public int[] coordsToPixel(Double lat, Double lng) {
		double adampixlng = 377;
		double adampixlat = 483;
		double apdpixlng = 636;
		double apdpixlat = 534;
		double adamlat = 5820.0;
		double adamlng = 6840.0;

		Double latperpixel = (double) ((5820 - 5256) / (apdpixlat - adampixlat));
		Double lngperpixel = (double) ((6840 - 10692.0) / (apdpixlng - adampixlng));
		// System.out.println("diflat2 = " + (52.35 - 50.85) + "\t\t\t" +
		// latperpixelmaastricht + " " + lngperpixelmaastricht);
		int pixels[] = new int[2];
		pixels[1] = (int) ((adamlat - lat) / latperpixel + adampixlat);// latperpixel
		pixels[0] = (int) ((adamlng - lng) / lngperpixel + adampixlng);// altperpixel
		return pixels;
	}

	/**
	 * Return a JLabel containing this Picture, for embedding in a JPanel,
	 * JFrame or other GUI widget.
	 */
	public JLabel getJLabel() {
		if (image == null) {
			return null;
		} // no image available
		ImageIcon icon = new ImageIcon(image);//.getScaledInstance(400, 600, 2 ));
		return new JLabel(icon);
	}

	/**
	 * Display the picture in a window on the screen.
	 */
	public JFrame show() {

		// create the GUI for viewing the image if needed
		if (frame == null) {
			frame = new JFrame();

			JMenuBar menuBar = new JMenuBar();
			JMenu menu = new JMenu("File");
			menuBar.add(menu);
			JMenuItem menuItem1 = new JMenuItem(" Save...   ");
			menuItem1.addActionListener(this);
			menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
					Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
			menu.add(menuItem1);
			frame.setJMenuBar(menuBar);
			frame.setContentPane(getJLabel());
			// f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
			frame.setTitle(filename);
			frame.setResizable(true);
			frame.pack();
			frame.setVisible(true);
		}

		// draw
		return frame;
	}

	/**
	 * Return the height of the picture in pixels.
	 */
	public int height() {
		return height;
	}

	/**
	 * Return the width of the picture in pixels.
	 */
	public int width() {
		return width;
	}

	/**
	 * Return the color of pixel (i, j).
	 */
	public Color get(int i, int j) {
		if (isOriginUpperLeft)
			return new Color(image.getRGB(i, j));
		else
			return new Color(image.getRGB(i, height - j - 1));
	}

	/**
	 * Set the color of pixel (i, j) to c.
	 */
	public void set(int i, int j, Color c) {
		if (c == null) {
			throw new RuntimeException("can't set Color to null");
		}
		try {
			if (isOriginUpperLeft)
				image.setRGB(i, j, c.getRGB());
			else
				image.setRGB(i, height - j - 1, c.getRGB());
		} catch (ArrayIndexOutOfBoundsException e) {
			if (i >= image.getWidth())
				i = image.getWidth() - 1;
			if (j >= image.getWidth())
				j = image.getHeight() - 1;
			if (i < 0)
				i = 0;
			if (j < 0)
				j = 0;
			if (isOriginUpperLeft)
				image.setRGB(i, j, c.getRGB());
			else
				image.setRGB(i, height - j - 1, c.getRGB());

		}
	}

	/**
	 * Is this Picture equal to obj?
	 */
	public boolean equals(Object obj) {
		if (obj == this)
			return true;
		if (obj == null)
			return false;
		if (obj.getClass() != this.getClass())
			return false;
		Picture that = (Picture) obj;
		if (this.width() != that.width())
			return false;
		if (this.height() != that.height())
			return false;
		for (int x = 0; x < width(); x++)
			for (int y = 0; y < height(); y++)
				if (!this.get(x, y).equals(that.get(x, y)))
					return false;
		return true;
	}

	/**
	 * Save the picture to a file in a standard image format. The filetype must
	 * be .png or .jpg.
	 */
	public void save(String name) {
		save(new File(name));
	}

	/**
	 * Save the picture to a file in a standard image format.
	 */
	public void save(File file) {
		this.filename = file.getName();
		if (frame != null) {
			frame.setTitle(filename);
		}
		String suffix = filename.substring(filename.lastIndexOf('.') + 1);
		suffix = suffix.toLowerCase();
		if (suffix.equals("jpg") || suffix.equals("png")) {
			try {
				ImageIO.write(image, suffix, file);
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else {
			System.out.println("Error: filename must end in .jpg or .png");
		}
	}

	/**
	 * Opens a save dialog box when the user selects "Save As" from the menu.
	 */
	public void actionPerformed(ActionEvent e) {
		FileDialog chooser = new FileDialog(frame,
				"Use a .png or .jpg extension", FileDialog.SAVE);
		chooser.setVisible(true);
		if (chooser.getFile() != null) {
			save(chooser.getDirectory() + File.separator + chooser.getFile());
		}
	}

	/**
	 * Gets as input 2 HashMaps, 1 with a key and an array of coordinates as the
	 * value, 1 with a key and a sentiment as value. Output is a HashMap with
	 * the keys that were in both the input HashMaps and an array with the
	 * coordinates and the sentiment as values.
	 */
	public HashMap<String, Double[]> compareMaps(
			HashMap<String, Double[]> nLplaces, HashMap<String, Double[]> nLMap) {
		HashMap<String, Double[]> henk = new HashMap<String, Double[]>();
		Iterator<String> k = nLMap.keySet().iterator();
		String key = null;
		Double[] sentiment;

		while (k.hasNext()) {

			Double[] coords = new Double[3];
			key = k.next();
			sentiment = nLMap.get(key);
			if (nLplaces.containsKey(key)) {
				// System.out.println(nLplaces.get(key).length);
				coords[0] = nLplaces.get(key)[0];
				coords[1] = nLplaces.get(key)[1];
				coords[2] = sentiment[0];
				henk.put(key, coords);
				// System.out.println("key "+key+" coords "+coords[0]+" "+coords[1]+" "+coords[2]);
			}

		}
		return henk;

	}

	public HashMap<String, Double[]> fileToHashMap(File file, boolean j) {
		HashMap<String, Double[]> output = new HashMap<String, Double[]>();
		BufferedReader reader = null;
		Double[] values = new Double[2];

		try {
			reader = new BufferedReader(new FileReader(file));
			String text = null;
			String tempcontents = null;
			String plaats = null;

			// repeat until all lines is read
			while ((text = reader.readLine()) != null) {
				StringBuffer contents = new StringBuffer();

				contents.append(text).append(
						System.getProperty("line.separator"));
				tempcontents = contents.toString();
				String key = null;

				plaats = "";
				Double f = 0.0;
				int i = 0;
				values = new Double[2];
				if (j) {
					tempcontents = tempcontents.substring(0,
							tempcontents.length() - 3);
				}
				// System.out.println(tempcontents.substring(0,
				// tempcontents.length()-3));
				StringTokenizer st = new StringTokenizer(tempcontents,
						" \r\n\t\f,!$%^&*()[]{}=\\;<>/?");
				// System.out.println(tempcontents);
				// System.out.println("=====================");
				// String stad = st.nextToken();
				// float number1 = Float.parseFloat(st.nextToken());
				// float number2 = Float.parseFloat(st.nextToken());
				while (st.hasMoreTokens()) {

					key = st.nextToken();

					// System.out.println(key);
					try {
						// //System.out.println("index"+i);
						f = Double.parseDouble(key);
						// System.out.println(f);
						values[i] = f;
						i++;
					} catch (Exception e) {
						// System.out.println(e);
						// /e.printStackTrace();
						// System.out.println(plaats+" "+key);
						plaats += " " + key;
					}

				}
				// System.out.println(values[0]+ " " + values[1]);
				output.put(plaats.replace(" ", "").toLowerCase(), values);

			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (reader != null) {
					reader.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return output;

	}

	/**
	 * Test client. Takes the coordinates and the sentiments out of the Hashmap,
	 * and prints them on the input image. Then shows the image in a window on
	 * the screen
	 */
	public static JFrame createPicture( HashMap<Integer, String[]> indexTweetSentiment, HashMap<Integer, String[]> indexAllData) {
		//0 tweet, 1 sentiment, 2 naam, 3 provincie, 4 stad
		HashMap<Integer, String[]> citySentiment = new HashMap<Integer, String[]>();
		Iterator<Integer> indexIterator = (Iterator<Integer>) indexTweetSentiment.keySet().iterator();
		System.out.println("CREATING NEW PICTURE");
		int i = 0;
		System.out.println(indexTweetSentiment.keySet().size() + "  " + indexAllData.keySet().size());
		while(indexIterator.hasNext())
		{
			i++;
			int index = indexIterator.next();
			if(indexAllData.containsKey(index))
			{
				String[] stadsentiment = new String[2];
				stadsentiment[0] = indexAllData.get(index)[4];
				stadsentiment[1] = indexTweetSentiment.get(index)[1];
				citySentiment.put(index, stadsentiment);
			}
		}
		uitExcel.toFile(citySentiment);
		Picture pic = new Picture("kerstdiner.jpg");
		File otherFile = new File("SentimentMap.txt");
		HashMap<String, Double[]> nLMap = readDBF.read("NL.DBF");
		HashMap<String, Double[]> nLplaces = pic
				.fileToHashMap(otherFile, false);
		HashMap<String, Double[]> input = pic.compareMaps(nLMap, nLplaces);
		Iterator<String> k = input.keySet().iterator();
		String key = null;
		// System.out.println(nLMap.size() + "  " + nLplaces.size() + " " +
		// input.size() );
		Double lat;
		Double lng;
		Double sentiment;
		Color color;

		while (k.hasNext()) {
			key = k.next();
			Double[] temp = new Double[3];
			int[] pixels = new int[2];
			temp = input.get(key);
			lat = temp[0];
			lng = temp[1];
			// lat = 52.633f;
			// lng = 4.75f;
			sentiment = temp[2];
			color = pic.sentimentToColor(sentiment);
			pixels = pic.coordsToPixel(lat, lng);
			setPixels(pic, pixels, color);
			
		}
		return pic.show();
	}

	private static void setPixels(Picture pic, int[] pixels, Color color) {
		
		if (pixels[0] >= 0 && pixels[0] <= pic.width - 1 && pixels[1] >= 0
				&& pixels[1] <= pic.height - 1) {
			int straal = 25;
			double fadeout = 0.8;
			List<int[]> pixelstoset = createPixelsToSet(pixels, straal);
			for(int i = 0; i < pixelstoset.size(); i++)
			{
				
				int[] pixeltoset = pixelstoset.get(i);
				if( pixeltoset[0] >= 0 && pixeltoset[0] <= pic.width - 1 && pixeltoset[1] >= 0
				&& pixeltoset[1] <= pic.height - 1){
				double distToMiddle = Math.sqrt(Math.abs(pixels[0] - pixeltoset[0]) * Math.abs(pixels[0] - pixeltoset[0]) + Math.abs(pixels[1] - pixeltoset[1]) * Math.abs(pixels[1] - pixeltoset[1])); 
				Color colorToSet = new Color((int) (color.getRed()*(1-fadeout*distToMiddle/straal)), (int) (color.getGreen()*(1-fadeout*distToMiddle/straal)), (int) (color.getBlue()*(1-fadeout*distToMiddle/straal)));
				Color curColor = pic.get(pixeltoset[0], pixeltoset[1]);
				if (curColor.getRed() + curColor.getGreen() + curColor.getBlue() < 50 ) //blackPIXEL
				{
					curColor = new Color(255,255,255);
				}
				//System.out.println("Pixel to set: " + pixeltoset[0] + " " + pixeltoset[1]);
				//System.out.println("color: " + color.getRed() + " " + color.getGreen() + " " + color.getBlue());
				//System.out.println("distToMiddle = " + distToMiddle);
				//System.out.println("colorToSet = " + (int) (color.getRed()*(1-fadeout*distToMiddle/straal)) + "  " +  (int) (color.getGreen()*(1-fadeout*distToMiddle/straal)) + " " + (int) (color.getBlue()*(1-fadeout*distToMiddle/straal)));
				//System.out.println("Curcolor = " + curColor.getRed() + " " + curColor.getGreen() + " " + curColor.getBlue());
				//System.out.println("-------&&&&-------");
				int newRed = curColor.getRed()-colorToSet.getRed();
				int newGreen = curColor.getGreen()-colorToSet.getGreen();
				int newBlue = curColor.getBlue()-colorToSet.getBlue();
				if(newRed < 0) newRed = 0;
				if(newGreen < 0) newGreen = 0;
				if(newBlue < 0) newBlue = 0;
				Color newColor = new Color(newRed, newGreen,newBlue);
				pic.set(pixeltoset[0], pixeltoset[1],newColor);
				}
			}
			
			
		}

	}

	private static List<int[]> createPixelsToSet(int[] pixels, int straal) {
		List<int[]> pixelstoset = new ArrayList<int[]>();
		int starti = pixels[0];
		int startj = pixels[1];
		for (int i = -straal; i <= straal; i++) {
			for (int j = -straal; j <= straal; j++) {
				if (Math.sqrt(i * i + j * j) <= straal) {
					int[] pixel = { i + starti, j + startj };
					pixelstoset.add(pixel);
				}
			}
		}
		return pixelstoset;

	}
	

}
